package lib

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"git.900sui.cn/kc/base/common/functions"
	"git.900sui.cn/kc/kcgin"
	"git.900sui.cn/kc/kcgin/logs"
	"git.900sui.cn/kc/mapstructure"
	"git.900sui.cn/kc/rpcPay/common/lib/gorsa"
	"git.900sui.cn/kc/rpcPay/common/lib/tool"
	"git.900sui.cn/kc/rpcPay/common/models"
	"git.900sui.cn/kc/rpcinterface/client/bus"
	orderClient "git.900sui.cn/kc/rpcinterface/client/order"
	bus2 "git.900sui.cn/kc/rpcinterface/interface/bus"
	"git.900sui.cn/kc/rpcinterface/interface/order"
	"git.900sui.cn/kc/rpcinterface/interface/pay"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/alipay"
	"github.com/shopspring/decimal"
	"github.com/tidwall/gjson"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

//接入方式
const (
	// 接入方式
	//0:Android SDK
	//1:iOS SDK
	//2:windows SDK
	//3:直连
	Access_Android_SDK = "0"
	Acess_iOS_SDK      = "1"
	Access_Windows_SDK = "2"
	Access_OPEN_SDK    = "3"
)

const (
	PayWayWx      = "1" //微信支付(小程序)
	PayWayAli     = "2" // 支付宝支付
	PayWayYinlian = "3" // 银联支付
)

//交易类型
const (
	TraTypeOffical = "5" //公众号
	TraTypeProgram = "8" //小程序
)

//新大陆支付业务逻辑

type xindalupay struct {
	privateKey              string //私钥证书
	publicKey               string //公钥证书
	driveNo                 string //码牌编号 自定义
	agetId                  string //机构号
	custId                  string //商户号
	wxMchid                 string //微信商户号
	wxAppid                 string //微信appid
	uniondPayUrl            string // 统一下单接口地址
	notifyUrl               string //回调地址-云收款 异步通知接口
	weChatGetwxInfoUrl      string // 公众号查询接口地址
	tradeQueryUrl           string //交易查询接口地址
	orderAccountUrl         string //订单分账 地址
	merchantAccountQueryUrl string //  商圈分账规则查询接口地址
	getPaypalTagUrl         string //获取银联用户接口地址
	userId                  string //小程序id
	aliAppid                string //支付宝支付appid
	wxNotifyUrl             string //微信通知地址
	wxUrl                   string //微信支付接口地址
	areaId                  string //商圈编号
	rw                      sync.RWMutex
	wxScret                 string //微信公众号 scret
	aliPrivatekey           string //支付宝的私钥key
}

const (
	//杭州
	CHANNELAREA_xindalu = 383 //支付地点
	//#支付渠道
	//平安云收款
	CHANNELPAY_xindalu = "XINDALUPAY" //支付机构

	DEFAULT_DRIVENO = "xindalupay" //默认的码牌编号
)

var Xindalupay *xindalupay

//生成自定义的码牌编号
func (x *xindalupay) getDriveNo() {
	if x.driveNo == "" {
		x.driveNo = DEFAULT_DRIVENO
	}
}

//初始化
func (x *xindalupay) Init(cid int) (err error) {
	//杭州地区参数化
	if cid == CHANNELAREA_xindalu {
		Xindalupay = &xindalupay{
			privateKey:              kcgin.AppConfig.String("xindalu.shPrivateKey"),
			publicKey:               kcgin.AppConfig.String("xindalu.shPublicKey"),
			agetId:                  kcgin.AppConfig.String("xindalu.shAgetId"),
			custId:                  kcgin.AppConfig.String("xindalu.shCustId"),
			driveNo:                 kcgin.AppConfig.String("xindalu.shDriveNo"),
			wxMchid:                 kcgin.AppConfig.String("xindalu.shWxMchid"),
			wxAppid:                 kcgin.AppConfig.String("xindalu.shWxAppid"),
			uniondPayUrl:            kcgin.AppConfig.String("xindalu.shUniondPayUrl"),
			notifyUrl:               kcgin.AppConfig.String("xindalu.shNotifyUrl"),
			weChatGetwxInfoUrl:      kcgin.AppConfig.String("xindalu.shWeChatGetwxInfoUrl"),
			tradeQueryUrl:           kcgin.AppConfig.String("xindalu.shTradeQueryUrl"),
			orderAccountUrl:         kcgin.AppConfig.String("xindalu.shOrderAccountUrl"),
			merchantAccountQueryUrl: kcgin.AppConfig.String("xindalu.shMerchantAccountQueryUrl"),
			getPaypalTagUrl:         kcgin.AppConfig.String("xindalu.shGetPaypalTagUrl"),
			userId:                  kcgin.AppConfig.String("xindalu.shUserId"),
			wxNotifyUrl:             kcgin.AppConfig.String("xindalu.shWxNotifyUrl"),
			wxUrl:                   kcgin.AppConfig.String("xindalu.shWxUrl"),
			aliAppid:                kcgin.AppConfig.String("xindalu.shAliAppid"),
			areaId:                  kcgin.AppConfig.String("xindalu.shAreaId"),
			wxScret:                 kcgin.AppConfig.String("xindalu.shWxScret"),
			aliPrivatekey:           kcgin.AppConfig.String("xindalu.shAliPayprivateKey"),
		}
	} else {
		err = errors.New("当前城市未开通新大陆支付通道配置")
		return
	}
	return
}

//Sha256加密
func (x *xindalupay) Sha256(src string) string {
	m := sha256.New()
	m.Write([]byte(src))
	res := hex.EncodeToString(m.Sum(nil))
	return res
}

//测试得到sha1256
func (x *xindalupay) GetSHA256(signMap map[string]interface{}) (shal256Str string, err error) {
	x.rw.Lock()
	defer x.rw.Unlock()
	keys := []string{}
	for k, _ := range signMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	strs := ""
	for _, v := range keys {
		//if signMap[v] == "" {
		//	continue
		//}
		strs += v + "=" + signMap[v].(string) + "&"
	}
	strs = strs[0 : len(strs)-1] //去掉最后一个 &
	logs.Info(strs)
	shal256Str = x.Sha256(strs) //对字符串进行sha256加密
	return shal256Str, nil
}

func (x *xindalupay) getSHA256(signMap map[string]interface{}) (shal256Str string, err error) {
	x.rw.Lock()
	defer x.rw.Unlock()
	keys := []string{}
	for k, _ := range signMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	strs := ""
	for _, v := range keys {
		//if signMap[v] == "" {
		//	continue
		//}
		strs += v + "=" + signMap[v].(string) + "&"
	}
	strs = strs[0 : len(strs)-1] //去掉最后一个 &
	logs.Info(strs)
	shal256Str = x.Sha256(strs) //对字符串进行sha256加密
	return shal256Str, nil
}

// FormatPrivateKey 格式化支付宝普通应用秘钥
func FormatPrivateKey(privateKey string) (pKey string) {
	var buffer strings.Builder
	buffer.WriteString("-----BEGIN RSA PRIVATE KEY-----\n")
	rawLen := 64
	keyLen := len(privateKey)
	raws := keyLen / rawLen
	temp := keyLen % rawLen
	if temp > 0 {
		raws++
	}
	start := 0
	end := start + rawLen
	for i := 0; i < raws; i++ {
		if i == raws-1 {
			buffer.WriteString(privateKey[start:])
		} else {
			buffer.WriteString(privateKey[start:end])
		}
		buffer.WriteByte('\n')
		start += rawLen
		end = start + rawLen
	}
	buffer.WriteString("-----END RSA PRIVATE KEY-----\n")
	pKey = buffer.String()
	return
}

// FormatPublicKey 格式化支付宝普通支付宝公钥
func FormatPublicKey(publicKey string) (pKey string) {
	var buffer strings.Builder
	buffer.WriteString("-----BEGIN PUBLIC KEY-----\n")
	rawLen := 64
	keyLen := len(publicKey)
	raws := keyLen / rawLen
	temp := keyLen % rawLen
	if temp > 0 {
		raws++
	}
	start := 0
	end := start + rawLen
	for i := 0; i < raws; i++ {
		if i == raws-1 {
			buffer.WriteString(publicKey[start:])
		} else {
			buffer.WriteString(publicKey[start:end])
		}
		buffer.WriteByte('\n')
		start += rawLen
		end = start + rawLen
	}
	buffer.WriteString("-----END PUBLIC KEY-----\n")
	pKey = buffer.String()
	return
}

//RSA加密
func (x *xindalupay) RSA_Encrypt(plainText string) (string, error) {
	//打开文件
	file, err := os.Open(x.publicKey)
	if err != nil {
		return "", err
	}
	defer file.Close()
	//读取文件的内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)
	//这里需要进行 格式化操作
	index := strings.Index(string(buf), "-----BEGIN")
	var formtatPubcliKey string
	if index == -1 {
		formtatPubcliKey = FormatPublicKey(string(buf))
	} else {
		formtatPubcliKey = string(buf)
	}
	pubenctypt, err := gorsa.PublicEncrypt(plainText, formtatPubcliKey)
	return pubenctypt, err
}

//RSA解密
//直接读取的方式
//cipherText []byte 加密的[]byte
//私钥的位置信息 私钥解密
func (x *xindalupay) RSA_Decrypt(cipherText string) (string, error) {
	//打开文件
	file, err := os.Open(x.privateKey)
	if err != nil {
		return "", err
	}
	defer file.Close()
	//获取文件内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)

	//这里需要进行 格式化操作
	index := strings.Index(string(buf), "-----BEGIN")
	var formtatPrivateiKey string
	if index == -1 {
		formtatPrivateiKey = FormatPrivateKey(string(buf))
	} else {
		formtatPrivateiKey = string(buf)
	}

	pridecrypt, err := gorsa.PriKeyDecrypt(cipherText, formtatPrivateiKey)
	return pridecrypt, err
}

//@param publicKey key
//@param plainText 明文
//RSA加密
//统一使用gorsa封装的加解密处理
func (x *xindalupay) encrypt(plainText string) (string, error) {
	//调用内部加密算法
	sign_byte, err := x.RSA_Encrypt(plainText)
	return sign_byte, err
}

//@param 需要解密的字符串
//@return string,error "",err
func (x *xindalupay) decrypt(plainText string) (string, error) {
	decode_byte, err := x.RSA_Decrypt(plainText) //rsa_decrpt 解密
	return decode_byte, err
}

//公钥解密
func (x *xindalupay) RSA_PublicDecryPt(cipherText string) (string, error) {
	//打开文件
	file, err := os.Open(x.publicKey)
	if err != nil {
		return "", err
	}
	defer file.Close()
	//获取文件内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)

	//这里需要进行 格式化操作
	index := strings.Index(string(buf), "-----BEGIN")
	var formtatPublicKey string
	if index == -1 {
		formtatPublicKey = FormatPublicKey(string(buf))
	} else {
		formtatPublicKey = string(buf)
	}
	pridecrypt, err := gorsa.PublicDecrypt(cipherText, formtatPublicKey)
	return pridecrypt, err
}

//私钥加密
func (x *xindalupay) RSA_PriKeyEncryPt(cipherText string) (string, error) {
	//打开文件
	file, err := os.Open(x.privateKey)
	if err != nil {
		return "", err
	}
	defer file.Close()
	//获取文件内容
	info, _ := file.Stat()
	buf := make([]byte, info.Size())
	file.Read(buf)
	//这里需要进行 格式化操作
	index := strings.Index(string(buf), "-----BEGIN")
	var formtatPrivateiKey string
	if index == -1 {
		formtatPrivateiKey = FormatPrivateKey(string(buf))
	} else {
		formtatPrivateiKey = string(buf)
	}
	pridecrypt, err := gorsa.PriKeyEncrypt(cipherText, formtatPrivateiKey)
	return pridecrypt, err
}

//后台类签名
func (x *xindalupay) sign(signMap map[string]interface{}) (sign string, err error) {
	shal256WithStr, _ := x.getSHA256(signMap)
	logs.Info("xsha1256:", shal256WithStr)
	sign, err = x.encrypt(shal256WithStr)
	logs.Info("xsign:", sign)
	return
}

//后台类签名
func (x *xindalupay) Sign(signMap map[string]interface{}) (sign string, err error) {
	shal256WithStr, _ := x.getSHA256(signMap)
	logs.Info("sha1256:", shal256WithStr)
	sign, err = x.encrypt(shal256WithStr)
	logs.Info("sign:", sign)
	return
}

//发送请求
func (x *xindalupay) RequestGet(url string) (res string, err error) {
	var request *http.Request
	request, err = http.NewRequest("GET", url, nil)
	if err != nil {
		return
	}
	request.Header.Set("Content-Type", "application/json;charset=utf-8")
	request.Header.Set("Accept-Encoding", "*/*")
	client := &http.Client{}
	var response *http.Response
	response, err = client.Do(request)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == http.StatusOK {
		body, _ := ioutil.ReadAll(response.Body)
		res = string(body)
		return
	}
	err = errors.New("request status:" + response.Status + ",url :" + url)
	return
}

//发送请求
func (x *xindalupay) Request(req []byte, url string) (res string, err error) {
	var request *http.Request
	request, err = http.NewRequest("POST", url, bytes.NewBuffer(req))
	if err != nil {
		return
	}
	request.Header.Set("Content-Type", "application/json;charset=utf-8")
	request.Header.Set("Content-Length", strconv.Itoa(len(req)))
	request.Header.Set("Accept-Encoding", "*/*")
	client := &http.Client{}
	var response *http.Response
	response, err = client.Do(request)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == http.StatusOK {
		body, _ := ioutil.ReadAll(response.Body)
		res = string(body)
		return
	}
	err = errors.New("request status:" + response.Status + ",url :" + url)
	return
}

//使用defer 抢占 变量周期
func (x *xindalupay) errorHandler() (err error) {
	defer func() {
		//如果错误是网络错误
		if e, ok := recover().(url.Error); ok {
			err = e.Err
		}
	}()
	return
}

//需要进行对请求进行 data 传递的参数
// map 进行拼接 签名得到 sign
//返回将这个结果 进行为json 进行加密
//data["agengid"=>,"version":"2.0",pdata:刚才加密的string]
//还是一样的处理方式
//所有的data map[string]interface map[string] = string 可以进行
//这样就很简单直接传递 map[string]string sign
//然后将得到 sign
//然后data 加上
//相当于sign 签名变了
//然后将参数 进行拼接加密
// data -> sign
// 变了 sign+encode 在一起返回一个字符串
//1.0 data -> sign
//然后加密string

//返回data key=value 形式字符串
func (x *xindalupay) keyValue(signMap map[string]interface{}) string {
	keys := []string{}
	for k, _ := range signMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	strs := ""
	for _, v := range keys {
		//if signMap[v] == "" {
		//	continue
		//}
		strs += v + "=" + signMap[v].(string) + "&"
	}
	strs = strs[0 : len(strs)-1] //去掉最后一个 &
	return strs
}

func (x *xindalupay) encrptSignV2(data map[string]interface{}) (string, error) {
	sign, err := x.sign(data)
	if err != nil {
		return "", err
	}
	data["sign"] = sign
	encry_str := x.keyValue(data)
	return x.RSA_Encrypt(encry_str)
}

//对外测试
func (x *xindalupay) EncrptSignV2(data map[string]interface{}) (string, error) {
	sign, err := x.sign(data)
	if err != nil {
		return "", err
	}
	data["sign"] = sign
	encry_str := x.keyValue(data)
	return x.RSA_Encrypt(encry_str)
}
func (x *xindalupay) urlParse(data map[string]interface{}) string {
	urlValue := url.Values{}
	for k, v := range data {
		urlValue.Set(k, v.(string))
	}
	return urlValue.Encode()
}

//请求v2.0版本的get请求信息
// data 为发送的请求query,body信息
func (x *xindalupay) ExecByRsaByGet(uri string, data map[string]interface{}, f ...func(map[string]interface{})) (*gjson.Result, error) {
	enctry_str, err := x.encrptSignV2(data) //进行处理加密的功能
	if err != nil {
		return nil, err
	}
	log.Printf("data-eq: %v", data) //传递的data 信息
	return_data := make(map[string]interface{})
	return_data["agetId"] = kcgin.AppConfig.String("xindalu.shAgetId")
	return_data["version"] = "2.0.0"
	return_data["pdata"] = enctry_str
	//处理组合返回的数据信息
	//进行handler
	keyValue := x.urlParse(return_data)
	uri += "?" + keyValue
	logs.Info("发送的body: %v", return_data)
	logs.Info("url: %s", uri)

	result, err := x.RequestGet(uri)
	if err != nil {
		return nil, err
	}
	rjson := gjson.Parse(result)
	log.Printf("rjson:%v", rjson.String())
	if rjson.Get("code").String() != "000000" {
		return nil, errors.New(rjson.Get("msg").String())
	}
	return &rjson, nil
}

// v2.0版本的请求信息
func (x *xindalupay) ExecByRsaPost(url string, data map[string]interface{}, f ...func(map[string]interface{})) (*gjson.Result, error) {
	enctry_str, err := x.encrptSignV2(data)
	if err != nil {
		return nil, err
	}
	log.Printf("data-eq: %v", data) //传递的data 信息
	return_data := make(map[string]interface{})
	return_data["agetId"] = kcgin.AppConfig.String("xindalu.shAgetId")
	return_data["version"] = "2.0.0"
	return_data["pdata"] = enctry_str
	return_dataBytes, err := json.Marshal(return_data)
	if err != nil {
		return nil, err
	}
	log.Printf("send body: %s", string(return_dataBytes))
	result, err := x.Request(return_dataBytes, url)
	if err != nil {
		return nil, err
	}
	rjson := gjson.Parse(result)
	log.Printf("rjson:%v", rjson.String())
	if rjson.Get("code").String() != "000000" {
		return nil, errors.New(rjson.Get("msg").String())
	}
	return &rjson, nil
}

// 执行
func (x *xindalupay) exec(url string, data map[string]interface{}, f ...func(map[string]interface{})) (*gjson.Result, error) {
	sign, err := x.sign(data)
	if err != nil {
		return nil, err
	}
	data["sign"] = sign
	if len(f) > 0 {
		f[0](data)
	}
	//for k, _ := range data {
	//	str := functions.StringsToJSON(data[k].(string))
	//	m := make(map[string]interface{}, 0)
	//	m2 := make([]map[string]interface{}, 0)
	//	err := json.Unmarshal([]byte(str), &m)
	//	if err == nil {
	//		data[k] = m
	//	}
	//	err = json.Unmarshal([]byte(str), &m2)
	//	if err == nil {
	//		data[k] = m2
	//	}
	//}
	log.Printf("data-eq: %v", data)
	//这里需要将对应的json进行编码处理 就是不再编码
	reqBytes, _ := json.Marshal(data)
	logs.Info("%s", string(reqBytes))
	result, err1 := x.Request(reqBytes, url)
	if err1 != nil {
		return nil, err1
	}
	rjson := gjson.Parse(result)
	log.Printf("rjson:%v", rjson.String())
	if rjson.Get("code").String() != "000000" {
		return nil, errors.New(rjson.Get("msg").String())
	}
	return &rjson, nil
}

/**
 * 微信小程序支付-新大陆支付
 */
func (x *xindalupay) PayWxProgram(payInfo *pay.PayInfo) (*gjson.Result, error) {
	transNo := payInfo.OrderSn
	amount, _ := decimal.NewFromString(payInfo.RealAmount)
	args := &pay.ArgsunionidOrder{}
	args.OrderNO = transNo
	args.Txamt = amount.Mul(decimal.NewFromInt(100)).String()
	args.TxamtOrder = amount.Mul(decimal.NewFromInt(100)).String() // 原订单金额（单位分
	args.Access = Access_OPEN_SDK
	args.Ip = payInfo.CreateIP
	args.Openid = payInfo.OpenId
	args.TraType = TraTypeProgram
	args.WxAppid = payInfo.AppId
	args.PrdOrderNo = transNo
	args.UserId = x.userId
	args.PayWay = PayWayWx
	if kcgin.KcConfig.RunMode == "prod" {
		cid, err := x.GetCustIdByBusid(payInfo.Cid)
		if err != nil {
			return nil, err
		}
		args.CustId = cid
	} else {
		args.CustId = x.custId
	}

	result, err1 := x.UnionidOrder(args)
	if err1 != nil {
		return nil, err1
	}

	//data := map[string]interface{}{
	//	"orderNo":    transNo,
	//	"title":      title,
	//	"driveNo":    x.driveNo,
	//	"agetId":     x.agetId,
	//	"custId":     x.custId,
	//	"txamt":      amount.Mul(decimal.NewFromInt(100)).String(),
	//   "txamtOrder":amount.Mul(decimal.NewFromInt(100)).String(),
	//	"access":     Access_OPEN_SDK,
	//	"ip":         payInfo.CreateIP,
	//	"openid":     payInfo.OpenId,
	//	"traType":    TraTypeProgram,
	//	"wxAppid":    payInfo.AppId,
	//	"prdOrderNo": transNo,
	//	"userId":     x.userId,
	//	"payWay":     PayWayWx,
	//}
	//result, err1 := x.unifiedOrder(data)
	//if err1 != nil {
	//	return nil, err1
	//}

	ret := result.Get("data")
	//返回信息 按照杉德的格式返回给前端
	params := map[string]string{
		"timeStamp": ret.Get("jsapiTimestamp").String(),
		"nonceStr":  ret.Get("jsapiNoncestr").String(),
		"signType":  ret.Get("jsapiSignType").String(),
		"paySign":   ret.Get("jsapiPaySign").String(),
		"package":   ret.Get("jsapiPackage").String(),
	}

	paramsByte, _ := json.Marshal(params)
	credential := map[string]string{
		"params": string(paramsByte),
	}
	credentialByte, _ := json.Marshal(credential)
	returnData := map[string]string{
		"credential": string(credentialByte),
	}
	returnDataBytes, _ := json.Marshal(returnData)
	res := gjson.ParseBytes(returnDataBytes)
	return &res, nil
}

//公众号支付
func (x *xindalupay) PayWxOfficial(payInfo *pay.PayInfo) (*gjson.Result, error) {
	transNo := x.getTransNo()
	//if payInfo.OrderSn !=""{
	//	transNo = payInfo.OrderSn //如果传递订单号则不出来
	//}
	payInfo.OrderSn = transNo
	amount, _ := decimal.NewFromString(payInfo.RealAmount)
	args := &pay.ArgsunionidOrder{}
	args.OrderNO = transNo
	args.Txamt = amount.Mul(decimal.NewFromInt(100)).String()
	args.TxamtOrder = amount.Mul(decimal.NewFromInt(100)).String() // 原订单金额（单位分
	args.Access = Access_OPEN_SDK
	args.Ip = payInfo.CreateIP
	args.Openid = payInfo.OpenId
	args.TraType = TraTypeOffical
	args.WxAppid = payInfo.AppId
	args.PrdOrderNo = transNo
	args.UserId = x.userId
	args.PayWay = PayWayWx
	//这里进行hanle
	if kcgin.KcConfig.RunMode == "prod" {
		cid, err := x.GetCustIdByBusid(payInfo.Cid)
		if err != nil {
			return nil, err
		}
		args.CustId = cid
	} else {
		args.CustId = x.custId
	}
	result, err1 := x.UnionidOrder(args)
	if err1 != nil {
		return nil, err1
	}

	//transNo := x.getTransNo()
	//x.getDriveNo() //初始化driverNo
	//amount, _ := decimal.NewFromString(payInfo.RealAmount)
	//title := "订单支付"
	//data := map[string]interface{}{
	//	"orderNo":    transNo,
	//	"title":      title,
	//	"driveNo":    x.driveNo,
	//	"agetId":     x.agetId,
	//	"custId":     x.custId,
	//	"txamt":      amount.Mul(decimal.NewFromInt(100)).String(),
	//	"txamtOrder": amount.Mul(decimal.NewFromInt(100)).String(), // 原订单金额（单位分
	//	"access":     Access_OPEN_SDK,
	//	"ip":         payInfo.CreateIP,
	//	"openid":     payInfo.OpenId,
	//	"traType":    TraTypeOffical,
	//	"wxAppid":    payInfo.AppId,
	//	"prdOrderNo": transNo,
	//	"userId":     x.userId,
	//	"payWay":     PayWayWx,
	//}
	//result, err1 := x.unifiedOrder(data)
	//if err1 != nil {
	//	return nil, err1
	//}

	ret := result.Get("data")
	//返回信息 按照杉德的格式返回给前端
	params := map[string]string{
		"timeStamp": ret.Get("jsapiTimestamp").String(),
		"nonceStr":  ret.Get("jsapiNoncestr").String(),
		"signType":  ret.Get("jsapiSignType").String(),
		"paySign":   ret.Get("jsapiPaySign").String(),
		"package":   ret.Get("jsapiPackage").String(),
	}

	paramsByte, _ := json.Marshal(params)
	credential := map[string]string{
		"params": string(paramsByte),
	}
	credentialByte, _ := json.Marshal(credential)
	returnData := map[string]string{
		"credential": string(credentialByte),
	}
	returnDataBytes, _ := json.Marshal(returnData)
	res := gjson.ParseBytes(returnDataBytes)
	return &res, nil
}

//统一下单接口
//统一下订单操作
//支付宝支付
//微信朱字符
//银联卡支付都走z这里
func (x *xindalupay) unifiedOrder(para map[string]interface{}) (result *gjson.Result, err error) {
	//出错补偿机制
	var a uint64 = 0
	for {
		if atomic.LoadUint64(&a) == 3 {
			//最多尝试3次
			return nil, errors.New("网关错误")
		}
		result, err = x.exec(x.uniondPayUrl, para)
		if err == nil {
			return
		}
		atomic.AddUint64(&a, 1)
	}
}

// 生成随机数字字符串
func (x *xindalupay) randomInt(n int) string {
	timeUnix := time.Now().Unix()
	timestamp := time.Unix(timeUnix, 0).Format("20060102150405")
	var letters = []rune(timestamp)
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

/**
 * 获取请求流水号
yyyyMMddHHmmssSSS+三位随机数字
*/
func (x *xindalupay) getTransNo() string {
	t := time.Now()
	s := t.Format("20060102150405.999999")
	s = strings.Replace(s, ".", "", -1)
	le := len(s)
	if le < 20 {
		need_le := 20 - le
		s += x.randomInt(need_le)
	}
	return s
}

//公众号查询
func (x *xindalupay) WcharGetWino(args *pay.ArgsGetwxInfo) (*gjson.Result, error) {
	data := map[string]interface{}{
		"agetId":    x.agetId,
		"custId":    x.custId,
		"timeStamp": x.TimeToStr(),
		"version":   "1.0.0",
	}
	log.Printf("data:%v", data)
	log.Println(x.weChatGetwxInfoUrl)
	return x.exec(x.weChatGetwxInfoUrl, data)
}

//调用order-rpc-获取pay.info 信息
func (x *xindalupay) OrderHandler(orderSn string) (*pay.PayInfo, error) {
	return x.PayInfo(orderSn)
}

//本地查询订单信息
func (x *xindalupay) PayInfo(orderSn string) (*pay.PayInfo, error) {
	log.Printf("orderNo: %s", orderSn)
	enviro := kcgin.KcConfig.RunMode
	if enviro == "dev" {
		return &pay.PayInfo{
			CreateIP:   "192.168.1.120",
			RealAmount: "33.9",
		}, nil
	} else {
		rpcOrderClient := new(orderClient.OrderPay).Init()
		ctx := context.Background()
		reply := &pay.PayInfo{}
		log.Printf("reply: %+v", reply)
		err := rpcOrderClient.GetPayInfoForRpc(ctx, &orderSn, reply)

		if err != nil {
			return nil, err
		}
		return reply, nil
	}
}

//获取新大陆的商户号 根据busid获取 商户号
func (x *xindalupay) GetCustIdByBusid(busid int) (string, error) {
	busRpcClient := new(bus.Depos).Init()
	defer busRpcClient.Close()
	reply := &bus2.ReplyGetBusDeposInfo{}
	err := busRpcClient.GetBusDeposInfoByBusid(context.Background(), &busid, reply)
	if err != nil {
		return "", err
	}
	logs.Info("custid: %s", reply.AcctInfo.XindaluCustId)
	return reply.AcctInfo.XindaluCustId, nil
}

//统一下单入库 内部使用方式 外部rpc-service 调用
func (x *xindalupay) UnionidOrderService(args *pay.ArgsunionidOrder) (res *gjson.Result, err error) {
	var wg sync.WaitGroup
	payInfo, err := x.OrderHandler(args.OrderNO) //订单处理
	if err != nil {
		return nil, err
	}
	if payInfo == nil {
		return nil, errors.New("订单不存在!")
	}
	//payinfo.orderSn
	payinfoOrderSn := args.OrderNO //
	data := make(map[string]interface{}, 0)
	x.getDriveNo()
	args.DriveNo = x.driveNo
	args.OrderNO = x.getTransNo() //订单号进行映射 payinfo ->本地订单号
	args.Title = "订单支付"
	args.AgetId = x.agetId
	//为了不影响测试环境测试
	if kcgin.KcConfig.RunMode == "prod" {
		//CustId hanle
		args.CustId, err = x.GetCustIdByBusid(payInfo.BusId)
		if err != nil {
			return nil, err
		}
	} else {
		args.CustId = x.custId
	}

	//CustId handle end
	//args.CustId = x.custId
	args.WxAppid = x.wxAppid
	args.Access = Access_OPEN_SDK
	//args.Ip = payInfo.CreateIP

	amount, _ := decimal.NewFromString(payInfo.RealAmount) //金额
	log.Printf("realAmount: %s", amount)
	args.Txamt = amount.Mul(decimal.NewFromInt(100)).String()
	log.Printf("Txamt: %s", args.Txamt)
	args.TxamtOrder = amount.Mul(decimal.NewFromInt(100)).String() // 原订单金额（单位分
	args.Zfbappid = x.aliAppid

	err = mapstructure.WeakDecode(args, &data)
	if err != nil {
		return nil, err
	}
	delete(data, "cid") //删除cid信息
	logs.Info("data: %v", data)
	logs.Info("url: %s", x.uniondPayUrl)
	res, err = x.exec(x.uniondPayUrl, data)
	if err == nil {
		wg.Add(1)
		go func(payOrderNum string, orderNo string) {
			defer wg.Done()
			InsertPayinfoOrderMapping(payOrderNum, orderNo, data)
		}(payinfoOrderSn, args.OrderNO)
	}
	wg.Wait()
	return
}

func InsertPayinfoOrderMapping(payOrderSn string, unionOrderSn string, args map[string]interface{}) {
	logs.Info("payOrderSn: %s, argsorderSn: %s", payOrderSn, unionOrderSn)
	pnm := new(models.PayXindaluOrderModel).Init()
	insert_data := make(map[string]interface{})
	insert_data[pnm.Field.F_order_sn] = payOrderSn
	insert_data[pnm.Field.F_xindalu_order_sn] = unionOrderSn
	insert_data[pnm.Field.F_starus] = models.StatusSuccess
	param, _ := json.Marshal(args)
	insert_data[pnm.Field.F_uoin_order_params] = string(param)
	ret := pnm.Insert(insert_data)
	logs.Info("插入数据库成功 : %d", ret)
}

//统一下单入库 内部使用方式
func (x *xindalupay) UnionidOrder(args *pay.ArgsunionidOrder) (res *gjson.Result, err error) {
	var wg sync.WaitGroup
	data := make(map[string]interface{}, 0)
	payinfoOrderSn := args.OrderNO //传递的payinfo订单号
	x.getDriveNo()
	args.DriveNo = x.driveNo
	args.Title = "订单支付"
	args.AgetId = x.agetId
	//args.CustId = x.custId
	args.OrderNO = x.getTransNo() //自己的订单号
	err = mapstructure.WeakDecode(args, &data)
	if err != nil {
		return nil, err
	}
	logs.Info("data: %v", data)
	logs.Info("url: %s", x.uniondPayUrl)
	res, err = x.exec(x.uniondPayUrl, data)
	if err == nil {
		wg.Add(1)
		go func(payOrderNum string, orderNo string) {
			defer wg.Done()
			InsertPayinfoOrderMapping(payOrderNum, orderNo, data)
		}(payinfoOrderSn, args.OrderNO)
	}
	wg.Wait()
	return
}

//合并两个map
func (x *xindalupay) arrayMergeMap(map1 map[string]interface{}, map2 map[string]interface{}) (maps map[string]interface{}) {
	maps = make(map[string]interface{})
	for k, v := range map1 {
		map2[k] = v
	}
	maps = map2
	return
}
func (x *xindalupay) getCommonData() map[string]interface{} {
	m := make(map[string]interface{})
	m["agetId"] = x.agetId
	m["custId"] = x.custId
	m["timeStamp"] = string(time.Now().Unix())
	m["version"] = "v1.0.0"
	return m
}

func (x *xindalupay) TimeToStr() string {
	tem := strconv.Itoa(int(time.Now().Unix()))
	return tem
}

// 交易查询接口
func (x *xindalupay) TradeQuery(args *pay.ArgsTradeQuery) (*gjson.Result, error) {
	args.Version = "1.0.0"
	args.TimeStamp = x.TimeToStr()
	data := map[string]interface{}{
		"orderNo":   args.OrderNo,
		"agetId":    x.agetId,
		"custId":    x.custId,
		"custLogin": args.CustLogin,
		"timeStamp": args.TimeStamp,
		"version":   args.Version,
	}
	log.Printf("data : %v", data)
	log.Println(x.tradeQueryUrl)
	return x.exec(x.tradeQueryUrl, data)
}

// 订单分账
//@param 额外参数使用 payinfo.PayExtra
// PayExtra areaId feeType
func (x *xindalupay) OrderAccount(payinfo *pay.ArgsOrderAccount) (*gjson.Result, error) {
	data := make(map[string]interface{})
	payinfo.AgetId = x.agetId
	payinfo.CustId = x.custId
	payinfo.AreaId = x.areaId
	payinfo.TimeStamp = x.TimeToStr()
	payinfo.Version = "1.0.0"
	payinfo.Remark = "订单分账查询:"

	//这里必须是 json进行解析
	bodyByes, err := json.Marshal(payinfo)
	if err != nil {
		return nil, errors.New("json解析错误")
	}
	err = json.Unmarshal(bodyByes, &data)
	if err != nil {
		return nil, errors.New("解析失败")
	}
	delete(data, "cid") //删除对应的cid地区信息
	delete(data, "goodsAccDetail")
	for k, v := range data {
		switch v.(type) {
		case map[string]interface{}:
			res, _ := json.Marshal(data[k])
			data[k] = functions.StringsToJSON(string(res))
		case []interface{}:
			res, _ := json.Marshal(data[k])
			data[k] = functions.StringsToJSON(string(res))
		case []map[string]interface{}:
			res, _ := json.Marshal(data[k])
			data[k] = functions.StringsToJSON(string(res))
		}
	}
	logs.Info("data-ins:  %v", data)
	logs.Info(x.orderAccountUrl)
	return x.exec(x.orderAccountUrl, data)
}
func (x *xindalupay) MerchantAccountQuery(payinfo *pay.ArgsMerchantAccountQuery) (*gjson.Result, error) {
	data := make(map[string]interface{})
	payinfo.AgetId = x.agetId
	payinfo.CustId = x.custId
	payinfo.AreaId = x.areaId
	payinfo.TimeStamp = x.TimeToStr()
	payinfo.Version = "1.0.0"
	err := mapstructure.WeakDecode(payinfo, &data)
	if err != nil {
		return nil, errors.New("解析失败")
	}
	delete(data, "cid") //删除data 信息中的cid信息
	return x.exec(x.merchantAccountQueryUrl, data)
}
func (x *xindalupay) notifyCheckSign(plainText string) (bool, error) {
	data := make(map[string]interface{})
	err := json.Unmarshal([]byte(plainText), &data)
	if err != nil {
		return false, err
	}
	var sign string
	keys := []string{}
	for k, _ := range data {
		if k == "sign" {
			sign = data[k].(string)
			continue
		}
		keys = append(keys, k)
	}
	sort.Strings(keys)
	strs := ""
	for _, v := range keys {
		//logs.Info("key : %s",v)
		//logs.Info("v %v",data[v])
		//logs.Info(reflect.TypeOf(data[v]))
		strs += v + "=" + data[v].(string) + "&"
	}
	strs = strs[0 : len(strs)-1] //去掉最后一个 &
	shal256Str := x.Sha256(strs) //对字符串进行sha256加密
	logs.Info("str:%s", strs)
	logs.Info("当前的shal:%s", shal256Str)
	logs.Info("当前sign: %s", sign)
	decodeSha256, err := x.RSA_PublicDecryPt(sign)
	if err != nil {
		return false, err
	}
	return decodeSha256 == shal256Str, nil
}

//异步回调InputData raw 数据
func (x *xindalupay) Notify(inputData string, channelPay string) (*gjson.Result, error) {
	dataJson := gjson.Parse(inputData)
	if dataJson.Exists() {
		maps := make(map[string]interface{})
		dataJson.ForEach(func(key, value gjson.Result) bool {
			//channelArea 和 channelPay 字段为每个异步通知结果中新增的字段
			if key.String() != "channelArea" && key.String() != "channelPay" {
				maps[key.String()] = value.String()
			}
			return true
		})
		//这里是统一聚合处理不需要进行判断类型
		if channelPay == CHANNELPAY_xindalu {
			//这里需要进行校验
			inputData, _ := json.Marshal(maps)
			if ok, err := x.notifyCheckSign(string(inputData)); !ok {
				return nil, err
			}
			return &dataJson, nil
		} else {
			return nil, errors.New("unkown notify channel pay")
		}
	} else {
		return nil, errors.New("verify data format err")
	}
}

/**
 * 微信app支付-直连
 */
func (x *xindalupay) PayWxApp(payInfo *pay.PayInfo) (*gjson.Result, error) {
	amount, _ := decimal.NewFromString(payInfo.RealAmount)
	totalAmount, _ := strconv.Atoi(amount.Mul(decimal.NewFromInt(100)).String())
	title := "订单支付"
	resData := wxPay{
		Appid:       x.wxAppid,
		Mchid:       x.wxMchid,
		Description: title,
		OutTradeNo:  payInfo.OrderSn,
		NotifyUrl:   x.wxNotifyUrl,
		Amount: wxPayAmount{
			Total:    totalAmount,
			Currency: "CNY",
		},
	}
	resByte, err := json.Marshal(resData)
	if err != nil {
		return nil, err
	}
	wxClient, err := tool.GetWXClient(payInfo.Cid)
	if err != nil {
		return nil, err
	}
	upPay, err := wxClient.Pay(context.Background(), x.wxUrl, string(resByte))
	if err != nil {
		return nil, err
	}
	upPayBytes, _ := json.Marshal(upPay)
	res := gjson.ParseBytes(upPayBytes)

	return &res, nil
}

/**
 * 支付宝app支付-直连
 */
func (x *xindalupay) PayAliApp(payInfo *pay.PayInfo) (*gjson.Result, error) {
	title := "订单支付"

	params := make(map[string]interface{})
	params["subject"] = title
	params["out_trade_no"] = payInfo.OrderSn
	params["total_amount"] = payInfo.RealAmount
	aliPayClient, err := tool.NewAliClient(payInfo.Cid)
	if err != nil {
		return nil, err
	}
	query, err := aliPayClient.GetQueryParams(params, "alipay.trade.app.pay")
	if err != nil {
		return nil, err
	}
	jsonRes := map[string]string{
		"sdk_string": string(query),
	}
	resBytes, _ := json.Marshal(jsonRes)
	res := gjson.ParseBytes(resBytes)
	return &res, nil
}

const (
	PayCodeField     = "payCode"
	PayAuthCodeField = "authCode"
)

func (x *xindalupay) GetPaypalTag(args *pay.ArgsGetPaypalTag) (*gjson.Result, error) {
	body := make(map[string]interface{}) //初始化处理
	body["agetId"] = x.agetId
	body["custId"] = x.custId
	body["ip"] = args.Ip
	body["payCode"] = args.PayCode
	body["authCode"] = args.AuthCode
	log.Printf("body : %v", body)
	log.Println(x.getPaypalTagUrl)
	return x.exec(x.getPaypalTagUrl, body)
}
func (x *xindalupay) KeyIsInMap(m map[string]interface{}, key string) (f bool) {
	if m == nil {
		f = false
	}
	keys := make([]string, 0)
	for k, _ := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	if i := sort.SearchStrings(keys, key); i < len(keys) {
		if key == keys[i] {
			f = true
		}
	}
	return

}

func (x *xindalupay) generateAliAuthorizedUrl(info *pay.PayInfo) (string, error) {
	fronUri := x.generateFrontUri(info)
	redirect_url := fmt.Sprintf(kcgin.AppConfig.String("xindalu.shAliAuthorize"), x.aliAppid, fronUri)
	return redirect_url, nil
}
func (x *xindalupay) generateFrontUri(info *pay.PayInfo) string {
	fronUri := kcgin.AppConfig.String("xindalu.shFrontUri") + kcgin.AppConfig.String("xindalu.shFronUriSuffiex")
	fronUri = strings.Replace(fronUri, "@", "#", -1)
	urlPara := url.Values{}
	urlPara.Set("choseType", strconv.Itoa(info.ChosePayType))
	urlPara.Set("orderSn", info.OrderSn)
	urlPara.Set("cid", strconv.Itoa(info.Cid))
	sufix := urlPara.Encode()
	fronUri = fronUri + "?" + sufix
	fronUri = url.QueryEscape(fronUri)
	return fronUri
}

//微信授权信息
func (x *xindalupay) generateWxAuthorizedUrl(info *pay.PayInfo) (string, error) {
	fronUri := x.generateFrontUri(info)
	redirect_url := fmt.Sprintf(kcgin.AppConfig.String("xindalu.wxAuthorize"), x.wxAppid, fronUri, kcgin.AppConfig.String("xindalu.shState"))
	redirect_url += "#wechat_redirect"
	return redirect_url, nil
}

//银联跳转地址 默认为https://api.900sui.cn/v1/pay/xindalu/yinlian?choseType=17&cid=383&orderSn=JS4700109164926205953
func (x *xindalupay) generateFrontUriFromYinlian(info *pay.PayInfo) string {
	fronUri := kcgin.AppConfig.String("xindalu.shYinlianRedirectUri")
	urlPara := url.Values{}
	urlPara.Set("choseType", strconv.Itoa(info.ChosePayType))
	urlPara.Set("orderSn", info.OrderSn)
	urlPara.Set("cid", strconv.Itoa(info.Cid))
	sufix := urlPara.Encode()
	fronUri = fronUri + "?" + sufix
	fronUri = url.QueryEscape(fronUri)
	return fronUri
}

//获取银联授权地址信息
func (x *xindalupay) generateYinlianAuthorizedUrl(info *pay.PayInfo) (string, error) {
	fronUri := x.generateFrontUriFromYinlian(info)
	redirect_url := fmt.Sprintf(kcgin.AppConfig.String("xindalu.shYinlianAutorize"), fronUri)
	//获取授权的地址
	fullUri := kcgin.AppConfig.String("xindalu.shYinlianFronUri") + "?re=" + redirect_url + kcgin.AppConfig.String("xindalu.shYinlianUriSuffiex")
	fullUri = strings.Replace(fullUri, "@", "#", -1)
	return fullUri, nil
}
func (x *xindalupay) PayQr(info *pay.PayInfo) (string, error) {
	switch info.ChosePayType {
	case order.CHOSE_PAY_TYPE_XINDALU_WX, order.CHOSE_PAY_TYPE_WX:
		return x.generateWxAuthorizedUrl(info)
	case order.CHOSE_PAY_TYPE_XINDALU_ALI, order.CHOSE_PAY_TYPE_ALI:
		return x.generateAliAuthorizedUrl(info)
	case order.CHOSE_PAY_TYPE_XINDALU_YINLIAN:
		return x.generateYinlianAuthorizedUrl(info)
	}
	return "", nil
}
func (x *xindalupay) GetOpenid(args *pay.ArgsGetOpenid) (*gjson.Result, error) {
	uri := fmt.Sprintf(kcgin.AppConfig.String("xindalu.shOpenidAcessTokenUrl"), x.wxAppid, x.wxScret, args.Code)
	result, err := x.RequestGet(uri)
	if err != nil {
		return nil, err
	}
	rjson := gjson.Parse(result)
	if rjson.Get("errcode").Exists() {
		return nil, errors.New(result)
	}
	return &rjson, nil
}
func (x *xindalupay) GetAliUserId(args *pay.ArgsGetAliUserInfo) (*gjson.Result, error) {
	privateKey := x.aliPrivatekey
	//初始化支付宝客户端
	//    appId：应用ID
	//    privateKey：应用秘钥
	//    isProd：是否是正式环境
	client := alipay.NewClient(x.aliAppid, privateKey, true)
	//配置公共参数
	client.SetCharset("utf-8").
		SetSignType(alipay.RSA2).
		SetPrivateKeyType(alipay.PKCS1)

	// 请求参数
	bm := make(gopay.BodyMap)
	// 接口权限值，目前只支持auth_user和auth_base两个值。具体说明看文档介绍
	bm.Set("code", args.Code)
	bm.Set("grant_type", args.GrantType)
	bm.Set("refresh_token", args.RefreshTtoken)
	// 发起请求
	aliRsp, err := client.SystemOauthToken(bm)
	if err != nil {
		return nil, err
	}
	if aliRsp == nil {
		return nil, errors.New("授权获取支付宝失败")
	}
	//进行返回reply即可
	reply := &pay.ReplyGetAliUserInfo{}
	reply.UserId = aliRsp.Response.UserId
	reply.AccessToken = aliRsp.Response.AccessToken
	reply.ExpiresIn = int(aliRsp.Response.ExpiresIn)
	reply.RefreshToken = aliRsp.Response.AccessToken
	reply.AlipayUserId = aliRsp.Response.AlipayUserId
	//返回即可
	gres, err := json.Marshal(reply)
	if err != nil {
		return nil, errors.New("解析错误")
	}
	ret := gjson.Parse(string(gres))
	return &ret, nil
}

//查询根据返回的第三方订单到平台的订单映射
//@param xinOrderSn pay_xindalu_order 表中xindalu_order_sn 字段信息
func (x *xindalupay) SelectThirdOrderSn(xinOrderSn string) (interface{}, error) {
	payOrderModel := new(models.PayXindaluOrderModel)
	res, err := payOrderModel.Init().SelectOrderSnByXinOrderSn(xinOrderSn)
	if err != nil {
		return nil, err
	}
	if v, ok := res[payOrderModel.Field.F_order_sn]; ok {
		return v, nil
	}
	return nil, nil
}
